import Axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from '@ohos/axios';
import { NetworkError } from '../base/NetworkError';
import { encodesParametersInURL, mutableCopy, NetworkFormData, NetworkRequest } from '../base/NetworkRequest'
import { NetworkResponse } from '../base/NetworkResponse'
import { NetworkClientCertManager } from '../common/NetworkClientCertManager';

/**
 * `Interceptor` 接口定义了拦截器的行为。拦截器旨在通过拦截和处理请求或响应来修改网络交互的过程。
 *
 * @typeParam R - 返回值的类型，默认为 `any`。
 * @param chain - `Chain` 实例，提供了对请求或响应进行进一步操作的能力。
 * @returns 返回一个 `Promise`，该 `Promise` 解析为 `NetworkResponse<R>`，代表网络交互的结果。
 */
interface Interceptor {
  intercept<R = any>(chain: Chain): Promise<NetworkResponse<R>>
}

/**
 * Chain接口定义了网络请求链的基本操作。
 * 该接口用于构建和处理网络请求的拦截、修改等流程。
 */
interface Chain {

  /**
   * 获取原始请求对象。
   * @returns {NetworkRequest} 返回网络请求的初始对象。
   */
  getOriginRequest(): NetworkRequest

  /**
   * 获取网络拦截器对象。
   * @returns {NetworkInterceptor} 返回用于拦截和修改网络请求的拦截器对象。
   */
  getNetInterceptor(): NetworkInterceptor

  /**
   * 创建并返回一个新的网络请求对象。
   * @returns {NetworkRequest} 返回一个新的网络请求实例。
   */
  requestI(): NetworkRequest

  /**
   * 处理并发送网络请求。
   * @param {NetworkRequest} request - 待处理的网络请求对象。
   * @returns {Promise<NetworkResponse<any>>} 返回一个Promise对象，该对象解析为网络响应结果。
   */
  proceedI<R = any>(request: NetworkRequest): Promise<NetworkResponse<R>>
}

class NetworkChain implements Chain {
  private originRequest: NetworkRequest
  private netInterceptor: NetworkInterceptor
  private request: NetworkRequest
  private index: number
  private interceptors: Array<Interceptor>

  constructor(originRequest: NetworkRequest, netInterceptor: NetworkInterceptor, request: NetworkRequest, interceptors: Array<Interceptor>, index: number = 0) {
    this.originRequest = originRequest
    this.netInterceptor = netInterceptor
    this.request = request
    this.interceptors = interceptors
    this.index = index
  }

  getOriginRequest(): NetworkRequest {
    return mutableCopy(this.originRequest)
  }

  getNetInterceptor(): NetworkInterceptor {
    return this.netInterceptor
  }

  requestI(): NetworkRequest {
    return mutableCopy(this.request)
  }

  proceedI<R = any>(request: NetworkRequest): Promise<NetworkResponse<R>> {
    let interceptor = this.interceptors[this.index]
    let next = new NetworkChain(this.originRequest, this.netInterceptor, request, this.interceptors, this.index + 1)
    return interceptor.intercept(next)
  }
}

class NetworkInterceptor {
  private interceptors: Array<Interceptor> = []

  constructor(interceptors: Array<Interceptor> = []) {
    this.interceptors = interceptors
  }

  addInterceptor(interceptor: Interceptor) {
    this.interceptors.push(interceptor)
  }

  removeInterceptor(interceptor: Interceptor) {
    let index = this.interceptors.findIndex((value) => {
      return value === interceptor
    })
    if (index >= 0) {
      this.interceptors.splice(index, 1)
    }
  }

  mutableCopy(): NetworkInterceptor {
    return new NetworkInterceptor(this.interceptors.slice())
  }

  intercept<R = any>(request: NetworkRequest): Promise<NetworkResponse<R>> {
    let interceptors = new Array<Interceptor>();
    if (!!this.interceptors && this.interceptors.length > 0) {
      Array.prototype.push.apply(interceptors, this.interceptors);
    }

    if (!!request.interceptors) {
      Array.prototype.push.apply(interceptors, request.interceptors);
    }

    interceptors.push(new CallInterceptor())
    let chain = new NetworkChain(request, this, request, interceptors)
    return new Promise<NetworkResponse<R>>(async (resolve, reject) => {
      chain.proceedI(chain.requestI()).then((response) => {
        resolve(response)
      }).catch((error) => {
        reject({ ...error, originRequest: request })
      })
    })
  }
}

/**
 * CallInterceptor类实现了Interceptor接口，利用axios 库处理网络请求。
 */
class CallInterceptor implements Interceptor {
  private static axios = Axios.create()

  async callHttp<R = ESObject>(request: NetworkRequest): Promise<NetworkResponse<R>> {
    let newRequest = mutableCopy(request)
    let config = toAxiosRequest(mutableCopy(request))
    let caPath = new NetworkClientCertManager().getCACertBy(config.url)

    if (caPath) {
      config.caPath = caPath
    }

    try {
      let axiosResponse: AxiosResponse<R> = await CallInterceptor.axios.request<R, AxiosResponse<R>>(config)
      let response: NetworkResponse<R> = {
        originRequest: request,
        disposeRequest: newRequest,
        data: axiosResponse.data
      }
      return Promise.resolve(response)
    } catch (axiosError) {
      let currentError: AxiosError<NetworkError> = axiosError as AxiosError<NetworkError>

      let networkError: NetworkError = currentError.response?.data as NetworkError

      let code = networkError?.code ?? currentError.response?.status

      let statusCode = networkError?.statusCode ?? currentError.response?.status

      let status = networkError?.status ?? currentError.response?.status

      let message = networkError?.message ?? currentError.message

      let developerMessage = networkError?.developerMessage

      let networkErr: NetworkError = {
        code,
        status,
        statusCode,
        message,
        developerMessage,
        data: networkError?.data ?? currentError.response?.data
      }
      return Promise.reject({ disposeRequest: request, error: networkErr })
    }
  }

  intercept<R = any>(chain: Chain): Promise<NetworkResponse<R>> {
    return this.callHttp(chain.requestI())
  }
}


/**
 * 创建并返回一个网络请求对象的可变副本。
 *
 * @param request - 一个表示网络请求的对象。
 * @returns 返回一个与原请求对象相同但独立的可变副本。
 */
function toAxiosRequest(request: NetworkRequest): AxiosRequestConfig {
  if (!encodesParametersInURL(request.method) && request.params && !request.data) {
    request.data = request.params;
    request.params = undefined;
  }
  if (isSpecCompliantForm(request.data)) {
    return { ...request, data: request.data.getFormData() }
  }
  return { ...request }
}

/**
 * 检查提供的 formData 是否是`NetworkFormData`类型。
 *
 * @param formData - 待检查的表单数据对象。需要是一个具有特定属性和方法的对象。
 * @returns 返回一个布尔值，如果提供的 formData 符合指定的规范，则为 true；否则为 false。
 */
function isSpecCompliantForm(formData): formData is NetworkFormData {
  return !!(formData && (typeof formData.getFormData === 'function') && formData[Symbol.toStringTag] === 'NetworkFormData' && formData[Symbol.iterator]);
}


export { Interceptor, Chain, NetworkInterceptor }